package com.acme.bankapp;

import java.util.Date;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;

import com.acme.bankapp.domain.bank.*;
import com.acme.bankapp.email.Email;
import com.acme.bankapp.email.EmailService;
import com.acme.bankapp.exceptions.*;
import com.acme.bankapp.network.BankServer;
import com.acme.bankapp.service.bank.*;

/**
 * This class tests BankService, Bank and other classes
 * 
 * @author Anton Kotov
 * 
 */
public class BankApplication {

    public static void main(String[] args) {
	Set<String> arguments = new HashSet<String>();
	for (String string : args) {
	    arguments.add(string);
	}

	boolean bankLoaded = false;
	Bank bank = null;

	try {
	    if (arguments.contains("-loadbank")) {
		bank = BankService.loadBank("bank.des");
		bankLoaded = true;
	    }

	    if (!bankLoaded) {
		bank = createInMemoryBank();
	    }

	    if (arguments.contains("-loadfeed")) {
		BankDataLoaderService.load(bank, "bank.dat");
	    }

	    BankService.saveBank("bank.ser", bank);

	    if (arguments.contains("-email")) {
		testEmailService();
	    }

	    if (arguments.contains("-statistics")) {
		printStatistics(bank);
	    }
	    
	    if (arguments.contains("-server")) {
		BankServer bankServer = new BankServer();
		bankServer.startServer(bank);
	    }

	} catch (Exception e) {
	    e.printStackTrace();
	}
    }

    private static void printStatistics(Bank bank) {
	System.out.println("-----statistics-----");
	System.out.println("Total number of clients: "
		+ BankReport.getNbOfCustomers(bank));
	System.out.println("--------------------");
	System.out.println("Total number of accounts: "
		+ BankReport.getNbOfAccounts(bank));
	System.out.println("--------------------");
	System.out.println("All accounts ordered by balance:");
	for (Account account : BankReport.getAccountsSortedBySum(bank)) {
	    System.out.println(account);
	}
	System.out.println("--------------------");
	System.out.println("Total credit: " + BankReport.getBankCredit(bank));
	System.out.println("--------------------");
	System.out.println("Map: client -> accounts");
	for (Entry<Client, Set<Account>> entry : BankReport.getCustomerAccount(
		bank).entrySet()) {
	    System.out.println(entry.getKey().getClientSalutation() + ":");
	    for (Account account : entry.getValue()) {
		System.out.println("\t" + account);
	    }
	}
    }

    private static Bank createInMemoryBank() {
	@SuppressWarnings("serial")
	Bank bank = new Bank(new ClientRegistrationListener() {

	    @Override
	    public void onClientAdded(Client client) {
		System.out.println(client.getClientSalutation());
	    }
	}, new ClientRegistrationListener() {

	    @Override
	    public void onClientAdded(Client client) {
		System.out.format(
			"Notification email for client '%s' to be sent%n",
			client.getClientSalutation());
	    }
	}, new ClientRegistrationListener() {

	    @Override
	    public void onClientAdded(Client client) {
		System.out.println(client.getClientSalutation() + " "
			+ new Date());
	    }
	});

	try {
	    BankService.addClients(bank, BankService.addAccounts(new Client(
		    "Bond", Gender.MALE), new CheckingAccount(0, 0.0, 1.0)),
		    BankService.addAccounts(new Client("Smith", Gender.FEMALE),
			    new SavingAccount(1, 0.0)), BankService
			    .addAccounts(new Client("Hood", Gender.MALE),
				    new CheckingAccount(2, 0.0, 1.0)),
		    BankService.addAccounts(new Client("John", Gender.FEMALE),
			    new SavingAccount(3, 0.0)), BankService
			    .addAccounts(new Client("Marion", Gender.MALE),
				    new CheckingAccount(4, 0.0, 1.0)),
		    BankService.addAccounts(new Client("Dale", Gender.FEMALE),
			    new SavingAccount(5, 0.0)), BankService
			    .addAccounts(new Client("Tuck", Gender.MALE),
				    new CheckingAccount(6, 0.0, 1.0)),
		    BankService.addAccounts(
			    new Client("Scarlet", Gender.FEMALE),
			    new SavingAccount(7, 0.0)), BankService
			    .addAccounts(new Client("Grant", Gender.MALE),
				    new CheckingAccount(8, 0.0, 1.0)),
		    BankService.addAccounts(new Client("Brown", Gender.FEMALE),
			    new SavingAccount(9, 0.0)));
	    BankService.addClients(bank, BankService.addAccounts(new Client(
		    "Bond", Gender.FEMALE), new SavingAccount(10, 0.0)));
	} catch (ClientExistsException e) {
	    System.out.println(e);
	}

	BankService.printBalance(bank);
	BankService.printMaximumAmountToWithdraw(bank);

	try {
	    BankService.modifyBank(bank);
	} catch (OverDraftLimitExceededException e) {
	    StringBuilder message = new StringBuilder();
	    message.append(e.getMessage());
	    for (Throwable t = e; (t = t.getCause()) != null;) {
		message.append("\n\tCause is: ");
		message.append(t.getMessage());
	    }
	    System.out.println(message);
	} catch (NotEnoughFundsException e) {
	    System.out.println(e.getMessage());
	}

	BankService.printBalance(bank);
	BankService.printMaximumAmountToWithdraw(bank);

	return bank;
    }

    private static void testEmailService() {
	EmailService service = new EmailService();
	// test empty service
	try {
	    Thread.sleep(5000);
	} catch (Exception e) {
	    e.printStackTrace();
	} finally {
	    service.close();
	}

	service = new EmailService();
	// test not empty service
	try {
	    for (int i = 0; i < 10; i++) {
		Email email = new Email(new Client("User_"
			+ new Random().nextDouble(), Gender.MALE), "To_"
			+ new Random().nextDouble());

		service.sendNotificationEmail(email);
	    }
	    Thread.sleep(5000);
	} catch (Exception e) {
	    e.printStackTrace();
	} finally {
	    service.close();
	}
    }
}
